#
# "@(#) $Id: CDLibrary.py,v 1.1.1.1 2006-07-27 03:16:08 gioshe Exp $"
#
# This work is released under the GNU GPL, version 2 or later.
#
from Library import *
from CDTrack import *
from Player import *
from Utils.Settings import *

from CD.CDROM import *
import Utils.cdinfo
import os
import warnings

def trim(s):
	if s==None: return None
	return s.strip()
#
# the singleton instance of the CD library
#
_CDLibraries = None
cdrom = None # persistent instance of pyxine.stream to work around xine_dispose() bug

class CDInfoThread(QThread):
	def __init__(self,library):
		QThread.__init__(self)
		self.library = library
	
	def run(self):
		self.library.beginFetch()
		self.library.fetchTracks()
		self.library.endFetch()

	def die(self):
		self.library.endFetch()
		self.terminate()

class CDLibrary(Library):
	def __init__(self,drive):
		Library.__init__(self)
		#print "building library for",drive.driver
		self.drive = drive # instance of CDROM
		QObject.connect(drive,PYSIGNAL("cdInserted"),self.insertedSync)
		QObject.connect(drive,PYSIGNAL("cdEjected"),self.ejected)
		self.name = "CDROM"
		self.dirty = True
		self.addNewPlaylist("CD")
		self.info = None
		self.thread = None

	#
	# see if we have info in cache, if not, load from online database
	#
	def loadInfo(self):
		return self.fetchCachedTracks()

	#
	# compute the path the CDDB cache directory
	#
	def cachePath(self):
		library = Library.mainLibrary()
		path = os.path.join(library.libraryPath(),".CDCache")
		library.ensureDirectory(path)
		return path
	
	#
	# compute the path to the file for this CDDB entry
	#
	def cachePathForID(self,disc_id):
		hexid = "%08lx" % disc_id[0]
		return os.path.join(self.cachePath(),hexid)

	#
	# check to see if this CD is in the cache
	#
	def fetchCachedTracks(self):
		path = self.cachePathForID(self.disc_id)
		if os.path.exists(path):
			file = open(path,"r")
			s = file.read()
			self.info = eval(s)
			file.close()
			return True
		return False

	#
	# write this CD's CDDB info into the cache
	#
	def writeCachedTracks(self,read_info):
		path = self.cachePathForID(self.disc_id)
		file = open(path,"w")
		file.write(str(read_info))
		file.close()

	#
	# fetch track info about this CD from the CDDB/FreeDB service
	# if we get it, save it in the cache
	#
	def fetchTracks(self):
		self.info = Utils.cdinfo.getCookedCDDBInfo(self.disc_id)
		if self.info!=None:
			self.writeCachedTracks(self.info)
			return True
		return False

	def trackAfter(self,track):
		trackNum = track.trackNum+1
		if trackNum>len(self._tracks):
			trackNum = 1
		return self.trackWithTrackNum(trackNum)
	
	def trackBefore(self,track):
		trackNum = track.trackNum-1
		if trackNum<1:
			trackNum = len(self._tracks)
		return self.trackWithTrackNum(trackNum)
	
	def trackWithTrackNum(self,trackNum):
		for trackKey in self._tracks.keys():
			track = self._tracks[trackKey]
			if track.trackNum==trackNum:
				return track
		return None

	def writeBinaryFile(self):
		pass
	
	def writeXMLFile(self):
		pass
	
	def insertedAsync(self,cdrom,firstTime = False):
		#print "CDLibrary - inserted",self.drive.driver
		pass
	
	def insertedSync(self,cdrom,firstTime = False):
		if cdrom==self.drive:
			self.firstTime = firstTime
			self.disc_id = Utils.cdinfo.disc_id(self.drive.driver)
			if self.loadInfo():
				self.insertedSyncDone()
			else:
				self.thread = CDInfoThread(self)
				print "starting thread"
				self.thread.start()
	
	def beginFetch(self):
		print "begin fetch"
		self.emit(PYSIGNAL("beginFetch"),(self,None))
	
	def endFetch(self):
		print "end fetch"
		self.emit(PYSIGNAL("endFetch"),(self,None))
		if not self.fetchCachedTracks() and LSettings.settings().get("CD Insertion Action","show")=='importAndEject':
			print "CD not found at FreeDB, ejecting"
			self.eject()
			self.thread = None
			return
		self.insertedSyncDone()
		self.thread = None
	
	def insertedSyncDone(self):
		self._tracks = {}
		total = self.drive.numTracks()
		timeOffset = 0
		for  index in xrange(total):
			track = self.importTrack(index,total)
			track.startTime = timeOffset
			timeOffset = timeOffset+track.totalTime
		self.emit(PYSIGNAL("cdMounted"),(self,self.drive,self.firstTime))

	def importTrack(self,index,total):
		track = CDTrack(self)
		track.loadFromCD(index+1)
		track.trackCount = total
		track.trackID = self.uniqueTrackID()
		self.addTrack(track)
		if track.album:
			self.name = track.album
		else:
			self.name = "CD"
		return track

	def killTasks(self):
		if self.thread:
			self.thread.die()
	
	def ejected(self):
		self.emit(PYSIGNAL("cdEjected"),(self,self.drive))
	
	def eject(self):
		return self.drive.eject()

	def clobber(self):
		self.dirty = True

	def enabledUnrippedTracks(self):
		result = []
		for track in self._tracks.values():
			if track.enabled and track.ripStatus=='idle':
				result.append(track)
		return result

	def rip(self):
		tracks = self.enabledUnrippedTracks()
		if len(tracks)>0:
			from LMusicRipper import LMusicRipper
			LMusicRipper.singleton().ripTracks(tracks)

class CDLibraries(QObject):
	def __init__(self):
		QObject.__init__(self)
		self._libraries = []
		self.buildLibraries()

	def buildLibraries(self):
		for drive in CDROMS.singleton().drives():
			library = CDLibrary(drive);
			self._libraries.append(library)
			QObject.connect(library.qObject(),PYSIGNAL("cdMounted"),self.mounted)
			QObject.connect(library.qObject(),PYSIGNAL("beginFetch"),self.beginFetch)
			QObject.connect(library.qObject(),PYSIGNAL("endFetch"),self.endFetch)
	
	def mounted(self,library,drive,firstTime):
		self.emit(PYSIGNAL("cdMounted"),(library,firstTime))
	
	def beginFetch(self,library):
		self.emit(PYSIGNAL("status"),({'Source':'Mounter','Status':'Begin','Library':library},None))
	
	def endFetch(self,library):
		self.emit(PYSIGNAL("status"),({'Source':'Mounter','Status':'End','Library':library},None))
	
	def eject(self):
		for library in self.libraries():
			library.eject()

	def killTasks(self):
		for library in self.libraries():
			library.killTasks()

	def libraries(self):
		return self._libraries

	def static_singleton():
		global _CDLibraries
		if _CDLibraries==None:
			_CDLibraries = CDLibraries()
		return _CDLibraries

	singleton = staticmethod(static_singleton)
	
